#include <pthread.h>
#include <unistd.h> // for sleep

#ifdef _WIN32
#define sleep(x) Sleep( x * 1000 )
#endif

#include "dbus-client.h"

using namespace std;

static const char *PIDBUS_BUSNAME = "im.pidgin.plugin.dbus";
static const char *PIDBUS_OBJPATH = "/im/pidgin/plugin/dbus";

DBus::BusDispatcher dispatcher;

void *dbus_mainloop_thread( void *args ){
	dispatcher.enter();
	return NULL;	
}

DBusClient* DBusClient::_instance = 0;

DBusClient* DBusClient::getInstance()
{
	if ( _instance == 0 )
	{
		try{
			DBus::_init_threading();
			DBus::default_dispatcher = &dispatcher;
			DBus::Connection conn = DBus::Connection::SessionBus();
			conn.request_name(PIDBUS_BUSNAME);
			_instance = new DBusClient(conn);
			pthread_t threads;
			pthread_create( &threads, NULL, dbus_mainloop_thread, NULL);
			sleep( 1 );
		}
		catch( DBus::Error e ){
			std::cerr << "InkDBus Error catched" << std::endl;                  
			_instance = NULL;
		}
	}
	return _instance;
}


DBusClient::DBusClient( DBus::Connection &connection )
: DBus::ObjectProxy(connection, PIDBUS_OBJPATH, PIDBUS_BUSNAME)
{
	// Initial
}

DBusClient::~DBusClient()
{
	// Initial
}


struct Args {                                                                                   
        std::string contact;
        std::string account;
};

/*
gboolean initialiseSessionCallback( gpointer data )                                       
{
	Args *args = (Args*)data;
	std::string contact = args->contact;
	std::string account = args->account;

	SessionManager::instance().initialiseSession( contact, State::WHITEBOARD_PEER, account );

	return FALSE;
}
*/

void *dial_thread( void *args ){
	int pi = (int) args;

	DBusClient* dc = DBusClient::getInstance();
	std::cerr << "dial [" << pi << "]" << std::endl;
	dc->dial( pi );
	std::cerr << "dial [" << pi << "]" << std::endl;

	string status = "";
	for ( int i = 0 ; status != "S_OK" && i <= 3 ; i++ ){
		sleep( 3 );
		status = dc->getStatus( pi );
	}
	
	if ( status == "S_OK" ){
		std::cerr << "dial successfully." << std::endl;

		std::cerr << "getContact [" << pi << "]" << std::endl;
		std::string contact = dc->getContact( pi );
		std::cerr << "getContact => " << contact << std::endl;

		std::cerr << "getAccount [" << pi << "]" << std::endl;
		std::string account = dc->getAccount( pi );
		std::cerr << "getAccount => " << account << std::endl;

		setTalkIdByToAndFrom( pi, contact, account );

		std::cerr << "initialiseSession begin" << std::endl;
		Args* arg_prt = new Args;
		arg_prt->contact = contact;
		arg_prt->account = account;
/*
		g_idle_add( (GSourceFunc)initialiseSessionCallback, arg_prt );                    
*/

		std::cerr << "initialiseSession end" << std::endl;
	}
	else {
		std::cerr << "dial failed." << std::endl;
	}

	return NULL;
}

void DBusClient::registeredTalk(const std::string& app, const int32_t& tid)
{
	std::cerr << "DBusClient::registeredTalk " << std::endl
                  << app << std::endl
                  << tid << std::endl;

	std::cerr << "before dial" << std::endl;
        pthread_t threads;
        pthread_create( &threads, NULL, dial_thread, (void*)tid );
	std::cerr << "after dial" << std::endl;
}

void *answer_thread( void *args ){
	int pi = (int) args;

	DBusClient* dc = DBusClient::getInstance();
		std::cerr << "getContact [" << pi << "]" << std::endl;
		std::string contact = dc->getContact( pi );
		std::cerr << "getContact => " << contact << std::endl;

		std::cerr << "getAccount [" << pi << "]" << std::endl;
		std::string account = dc->getAccount( pi );
		std::cerr << "getAccount => " << account << std::endl;

		setTalkIdByToAndFrom( pi, contact, account );
	std::cerr << "answer [" << pi << "]" << std::endl;
	sleep( 1 );
	dc->answer( pi );
	std::cerr << "answer [" << pi << "]" << std::endl;

	return NULL;
}


void DBusClient::recvTalk(const std::string& app, const int32_t& tid)
{
	std::cerr << "DBusClient::recvTalk " << std::endl
                  << app << std::endl
                  << tid << std::endl;

	std::cerr << "before answer" << std::endl;
	// this->answer( tid ); // Don't call member function in member funtions.
        pthread_t threads;
        pthread_create( &threads, NULL, answer_thread, (void*)tid );

	std::cerr << "after answer" << std::endl;
}

void DBusClient::recvMessage(const int32_t& tid, const std::string& text)
{
	std::cerr << "DBusClient::recvMessage" << std::endl
                  << tid << std::endl
                  << text << std::endl;

	//receiveProcess( text );
}


struct SessionPair {
	int tid;
	std::string from;
	std::string to;
};

std::vector<SessionPair> talksMapping;

void setTalkIdByToAndFrom( const int tid, const std::string to, const std::string from )
{

	SessionPair spair;
	spair.tid = tid;
	spair.from = from;
	spair.to = to;
	
	talksMapping.push_back( spair );

}

bool isJIDsimilar( string left, string right )                                                  
{
        bool result=false;

        size_t foundL = left.find(right);
        size_t foundR = right.find(left);

        if ( foundL != string::npos || foundR!= string::npos ){
                result = true;
        }

        return result;
}

int getTalkIdByToAndFrom( const std::string to, const std::string from )
{
	int result = NULL;

	std::vector<SessionPair>::iterator iter;
	for ( iter = talksMapping.begin(); iter != talksMapping.end(); iter++ ){
		
		bool isToSimilar = isJIDsimilar( iter->to, to );
		bool isFromSimilar = isJIDsimilar( iter->from, from );

		if ( isToSimilar && isFromSimilar ){
			result = iter->tid;
		}
	}

	return result;
}

